#define NOTRACEOUT
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace EMailClient
{
    public class POPClient : ClientBase , IPOP3Handler
    {
        private const string COMMAND_QUIT = "QUIT";
        private const string COMMAND_RESET = "RSET";
        private const string COMMAND_NOOP = "NOOP";
        private const string COMMAND_UIDL = "UIDL";
        private const string COMMAND_STAT = "STAT";
        private const string COMMAND_LIST = "LIST";
        private const string COMMAND_UIDL_SINGLE = "UIDL {0}";
        private const string COMMAND_TOP = "TOP {0} 0";
        private const string COMMAND_USERNAME = "USER {0}";
        private const string COMMAND_PASSWORD = "PASS {0}";
        private const string COMMAND_DELETE = "DELE {0}";
        private const string COMMAND_RESULT_LONGEND = "\r\n.\r\n";

        public POPClient(string address, string userName, string password)
            : this(address, 110, userName, password) { }

        public POPClient(string address, int port, string userName, string password)
            : base(address, port, userName, password) { }

        private bool m_ReceiveLongData = false;

        public bool Reset()
        {
            return IsCorrectResponse(SendCommand(COMMAND_RESET));
        }

        public bool Noop()
        {
            return IsCorrectResponse(SendCommand(COMMAND_NOOP));
        }

        public override bool AuthLogin()
        {
            if (Connected)
            {
                if (IsCorrectResponse(SendCommand(string.Format(COMMAND_USERNAME,m_UserName)))
                    && IsCorrectResponse(SendCommand(string.Format(COMMAND_PASSWORD,m_Password))))
                {
                    return true;
                }
                else
                {
                    this.Reset();                    
                }
            }
            return false;
        }

        protected override bool Quit()
        {
            if (Connected)
            {
                return IsCorrectResponse(SendCommand(COMMAND_QUIT));
            }
            return false;
        }

        protected override bool IsCorrectResponse(string response)
        {
            return response.StartsWith("+OK");
        }

        private bool EndOfLongResponse(StringBuilder builder)
        {
            return builder[builder.Length - 1] == '\n'
                && builder[builder.Length - 2] == '\r'
                && builder[builder.Length - 3] == '.'
                && builder[builder.Length - 4] == '\n'
                && builder[builder.Length - 5] == '\r';
        }

        protected override string ReveiveResponse()
        {
            if (m_ReceiveLongData)
            {
                m_ReceiveLongData = false;
                byte[] resultBuffer = new byte[1024];
                StringBuilder builder = new StringBuilder();
                while (true)
                {
                    int read = m_ConnectStream.Read(resultBuffer, 0, resultBuffer.Length);
                    string str = System.Text.Encoding.ASCII.GetString(resultBuffer, 0, read);
                    builder.Append(str);
                    if (str.EndsWith(COMMAND_RESULT_LONGEND) || EndOfLongResponse(builder))
                    {
                        break;
                    }
                }
#if TRACEOUT
                Console.Write(builder.ToString());
#endif
                return builder.ToString();
            }
            else
            {
                return base.ReveiveResponse();
            }
        }

        public SortedList<int,string> GetUIDList()
        {
            SortedList<int, string> result = new SortedList<int,string>();
            if (Connected)
            {
                m_ReceiveLongData = true;
                string value = SendCommand(COMMAND_UIDL);
                if (IsCorrectResponse(value))
                {
                    string[] lines = value.Split(new char[] { '\r', '\n' },StringSplitOptions.None);
                    if (lines.Length > 2)
                    {
                        for (int i = 0; i<lines.Length ;i++)
                        {
                            string[] subItem = lines[i].Split(' ');
                            if (subItem.Length == 2 && subItem[1].Length > 0)
                            {
                                int index = int.Parse(subItem[0]);
                                result.Add(index, subItem[1]);
                            }
                        }
                    }
                }
            }
            return result;
        }

        public string GetUIDL(int mailID)
        {
            return GetUIDL(mailID.ToString());
        }

        public string GetUIDL(string mailID)
        {
            if (Connected)
            {
                string value = SendCommand(string.Format(COMMAND_UIDL_SINGLE,mailID));
                if (IsCorrectResponse(value))
                {
                    string[] subItem = value.Split(' ');
                    return subItem[2];
                }
            }
            return null;
        }
        
        public int GetCount()
        {
            if (Connected)
            {
                string value = SendCommand(COMMAND_STAT);
                if (IsCorrectResponse(value))
                {
                    string[] subItem = value.Split(' ');
                    return int.Parse(subItem[1]);
                }
            }
            return -1;
        }

        public EMail GetHeader(int mailID)
        {
            return GetHeader(mailID.ToString());
        }

        public EMail GetHeader(string mailID)
        {
            string uid = GetUIDL(mailID);
            if (uid == null)
            {
                return null;
            }
            EMail mail = new EMail();
            m_ReceiveLongData = true;
            string value = SendCommand(string.Format(COMMAND_TOP,mailID));
            if (!IsCorrectResponse(value))
            {
                return null;
            }
            mail.UId = uid;
            mail.Header = value.Substring(value.IndexOf("\r\n") + 2);
            mail.SplitHeader();
            return mail;
        }

        public bool DeleteMail(int mailID)
        {
            return DeleteMail(mailID.ToString());
        }

        public bool DeleteMail(string  mailId)
        {
            return IsCorrectResponse(SendCommand(string.Format(COMMAND_DELETE,mailId)));
        }
    }
}
